package service

import (
	"bytes"
	"cgs-server/helper"
	"cgs-server/server/base"
	"cgs-server/server/model"
	"cgs-server/server/store"
	"cgs-server/server/upload"
	"errors"
	"fmt"
	uuid "github.com/satori/go.uuid"
	"mime/multipart"
	"path/filepath"
	"strings"
	"time"
)

type UploadSrv interface {
	Upload(file *multipart.FileHeader, mimeType string, user *model.User) (*model.PutFileResult, error)
}

type uploadSrv struct {
	store store.Factory
}

func (u *uploadSrv) Upload(file *multipart.FileHeader, mimeType string, user *model.User) (*model.PutFileResult, error) {
	if user == nil {
		return nil, errors.New("permission denied")
	}

	var (
		s1  string
		s2  string
		err error
	)

	switch mimeType {
	case "model":
		// 模型文件，zip 类型的文件
		s1, s2, err = u.uploadModel(file)
		if err != nil {
			return nil, err
		}
	default:
		s1, err = u.uploadFile(file, mimeType)
		if err != nil {
			return nil, err
		}
	}

	// todo 保存的文件路径问题
	return &model.PutFileResult{
		Link:         s1,
		Domain:       "",
		UnZipLink:    s2,
		Name:         filepath.Base(s1),
		OriginalName: file.Filename,
	}, nil
}

func (u *uploadSrv) uploadFile(file *multipart.FileHeader, mimeType string) (string, error) {
	if mimeType == "" {
		mimeType = "file"
	}
	fileStore := upload.NewFileStore(upload.MinIO)
	fileType := file.Header.Get("Content-Type")
	open, err := file.Open()
	if err != nil {
		return "", err
	}
	defer open.Close()
	fileExt := filepath.Ext(file.Filename)
	now := time.Now()
	v4, err := uuid.NewV4()
	if err != nil {
		return "", err
	}
	u1 := v4.String()
	u1 = strings.ReplaceAll(u1, "-", "")
	path := fmt.Sprintf("public/%s/%s/%s%s", mimeType, helper.TimeToString(now, "yyyyMMdd"), u1, fileExt)
	s, err := fileStore.Upload(open, path, file.Size, fileType)
	if err != nil {
		return "", err
	}
	return s, nil
}

func (u *uploadSrv) uploadModel(file *multipart.FileHeader) (string, string, error) {
	fileStore := upload.NewFileStore(upload.MinIO)
	if fileStore == nil {
		return "", "", errors.New("store client is nil")
	}
	fileType := file.Header.Get("Content-Type")
	open, err := file.Open()
	if err != nil {
		return "", "", err
	}
	defer open.Close()
	fileExt := filepath.Ext(file.Filename)
	now := time.Now()
	v4, err := uuid.NewV4()
	if err != nil {
		return "", "", err
	}
	u1 := v4.String()
	u1 = strings.ReplaceAll(u1, "-", "")
	// 1. 上传 zip 文件
	path := fmt.Sprintf("public/model/%s/%s%s", helper.TimeToString(now, "yyyyMMdd"), u1, fileExt)
	s, err := fileStore.Upload(open, path, file.Size, fileType)
	if err != nil {
		return "", "", err
	}
	// 2. 解压文件并上传
	zipData, err := base.ReadZip(open, file.Size)
	if err != nil {
		return "", "", err
	}
	var modelPath string
	hasEntry := false
	for _, datum := range zipData {
		ext := filepath.Ext(datum.Name)
		fn := strings.TrimSuffix(filepath.Base(datum.Name), ext)
		path = fmt.Sprintf("public/unzip/%s/%s", u1, datum.Name)
		s2, err := fileStore.Upload(bytes.NewReader(datum.Body), path, int64(len(datum.Body)), "")
		if err != nil {
			return "", "", err
		}
		// 如果是这两个的话，就用这个文件作为入口
		if strings.HasSuffix(fn, "_entry") || fn == "entry" {
			modelPath = s2
			hasEntry = true
		}
		if !hasEntry && (len(zipData) == 1 || len(strings.Split(datum.Name, "/")) == 1) {
			modelPath = s2
		}
	}

	return s, modelPath, nil
}

func newUploadSrv(s *service) UploadSrv {
	return &uploadSrv{
		store: s.store,
	}
}
